package asapi

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"jgt-sdk/utils"
)

// Token 令牌信息
type Token struct {
	AccessToken string    `json:"access_token"`
	ExpiresIn   int       `json:"expires_in"`
	CreateTime  time.Time `json:"-"`
}

// NewTokenHandle 创建令牌验证
func NewTokenHandle(cfg *Config) *TokenHandle {
	return &TokenHandle{
		cfg: cfg,
	}
}

// TokenHandle 令牌验证处理
type TokenHandle struct {
	sync.Mutex
	cfg   *Config
	token *Token
}

// ForceGet 强制获取最新的令牌数据
func (th *TokenHandle) ForceGet(traceID string) (*Token, error) {
	body := make(url.Values)
	body.Set("grant_type", "client_credentials")

	resp, err := utils.Request(nil, th.cfg.GetURL("/oauth2/token"), "POST", strings.NewReader(body.Encode()), func(req *http.Request) (*http.Request, error) {
		req.SetBasicAuth(th.cfg.ClientID, th.cfg.ClientSecret)
		req.Header.Set(utils.TraceIDKey, traceID)
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		return req, nil
	})
	if err != nil {
		return nil, err
	} else if resp.Response().StatusCode != 200 {
		v, verr := resp.String()
		if verr != nil {
			return nil, verr
		}
		return nil, fmt.Errorf(v)
	}

	var t Token
	err = resp.JSON(&t)
	if err != nil {
		return nil, err
	}

	t.CreateTime = time.Now()
	return &t, nil
}

// GetWithTraceID 获取令牌
func (th *TokenHandle) GetWithTraceID(traceID string) func() (string, error) {
	return func() (string, error) {
		th.Lock()
		defer th.Unlock()
		if th.token == nil ||
			th.token.CreateTime.Add(time.Duration(th.token.ExpiresIn-10)*time.Second).Before(time.Now()) {
			token, err := th.ForceGet(traceID)
			if err != nil {
				return "", err
			}
			th.token = token
		}

		return th.token.AccessToken, nil
	}
}

// Get 获取令牌
func (th *TokenHandle) Get() (string, error) {
	return th.GetWithTraceID(utils.UUIDString())()
}
